import * as THREE from "three"

import TransformControlsPlane from "./mesh/TransformControlsPlane"

const s = new THREE.Raycaster()
const r = new THREE.Vector3()
const a = new THREE.Vector3()
const o = new THREE.Quaternion()
const l = {
  X: new THREE.Vector3(1, 0, 0),
  Y: new THREE.Vector3(0, 1, 0),
  Z: new THREE.Vector3(0, 0, 1)
}
const h = { type: "change" }
const d = { type: "mouseDown", mode: null }
const c = { type: "mouseUp", mode: null }
const u = { type: "objectChange" }
export default class TransformControls extends THREE.Object3D {
  isTransformControls: boolean
  domElement: HTMLElement | Document
  _gizmo: TransformControlsGizmo
  _plane: TransformControlsPlane
  camera: THREE.OrthographicCamera
  object: any
  enabled: boolean
  axis: string
  mode: string
  translationSnap: any
  rotationSnap: any
  scaleSnap: any
  space: string
  size: number
  dragging: boolean
  showX: boolean
  showY: boolean
  showZ: boolean
  worldPosition: THREE.Vector3
  worldPositionStart: THREE.Vector3
  cameraPosition: THREE.Vector3
  pointStart: THREE.Vector3
  pointEnd: THREE.Vector3
  rotationAxis: THREE.Vector3
  rotationAngle: number
  eye: THREE.Vector3
  worldQuaternion: THREE.Quaternion
  worldQuaternionStart: THREE.Quaternion
  cameraQuaternion: THREE.Quaternion
  _offset: THREE.Vector3
  _startNorm: THREE.Vector3
  _endNorm: THREE.Vector3
  _cameraScale: THREE.Vector3
  _parentPosition: THREE.Vector3
  _parentQuaternion: THREE.Quaternion
  _parentQuaternionInv: THREE.Quaternion
  _parentScale: THREE.Vector3
  _worldScaleStart: THREE.Vector3
  _worldQuaternionInv: THREE.Quaternion
  _worldScale: THREE.Vector3
  _positionStart: THREE.Vector3
  _quaternionStart: THREE.Quaternion
  _scaleStart: THREE.Vector3
  _getPointer: typeof getPointer
  _onPointerDown: typeof onPointerDown
  _onPointerHover: typeof onPointerHover
  _onPointerMove: typeof onPointerMove
  _onPointerUp: typeof onPointerUp
  constructor(t: THREE.Camera, e: HTMLElement | Document = document) {
    super()
    this.isTransformControls = true
    this.visible = !1
    this.domElement = e
    this._gizmo = new TransformControlsGizmo()
    this.add(this._gizmo)
    this._plane = new TransformControlsPlane()
    this.add(this._plane)
    const a = (key: string, initVal: any) => {
      let oldVal = initVal
      Object.defineProperty(this, key, {
        get: () => {
          return void 0 !== oldVal ? oldVal : initVal
        },
        set: (value: any) => {
          if (oldVal !== value) {
            oldVal = value
            s[key] = value
            this._gizmo[key] = value
            this.dispatchEvent({ type: key + "-changed", value })
            this.dispatchEvent(h)
          }
        }
      })
      this[key] = e
      this._plane[key] = e
      this._gizmo[key] = e
    }
    a("camera", t)
    a("object", void 0)
    a("enabled", !0)
    a("axis", null)
    a("mode", "translate")
    a("translationSnap", null)
    a("rotationSnap", null)
    a("scaleSnap", null)
    a("space", "world")
    a("size", 1)
    a("dragging", !1)
    a("showX", !0)
    a("showY", !0)
    a("showZ", !0)
    const worldPosition = new THREE.Vector3()
    const worldPositionStart = new THREE.Vector3()
    const worldQuaternion = new THREE.Quaternion()
    const worldQuaternionStart = new THREE.Quaternion()
    const cameraPosition = new THREE.Vector3()
    const cameraQuaternion = new THREE.Quaternion()
    const pointStart = new THREE.Vector3()
    const pointEnd = new THREE.Vector3()
    const rotationAxis = new THREE.Vector3()
    const eye = new THREE.Vector3()
    a("worldPosition", worldPosition)
    a("worldPositionStart", worldPositionStart)
    a("worldQuaternion", worldQuaternion)
    a("worldQuaternionStart", worldQuaternionStart)
    a("cameraPosition", cameraPosition)
    a("cameraQuaternion", cameraQuaternion)
    a("pointStart", pointStart)
    a("pointEnd", pointEnd)
    a("rotationAxis", rotationAxis)
    a("rotationAngle", 0)
    a("eye", eye)
    this._offset = new THREE.Vector3()
    this._startNorm = new THREE.Vector3()
    this._endNorm = new THREE.Vector3()
    this._cameraScale = new THREE.Vector3()
    this._parentPosition = new THREE.Vector3()
    this._parentQuaternion = new THREE.Quaternion()
    this._parentQuaternionInv = new THREE.Quaternion()
    this._parentScale = new THREE.Vector3()
    this._worldScaleStart = new THREE.Vector3()
    this._worldQuaternionInv = new THREE.Quaternion()
    this._worldScale = new THREE.Vector3()
    this._positionStart = new THREE.Vector3()
    this._quaternionStart = new THREE.Quaternion()
    this._scaleStart = new THREE.Vector3()
    this._getPointer = getPointer.bind(this)
    this._onPointerDown = onPointerDown.bind(this)
    this._onPointerHover = onPointerHover.bind(this)
    this._onPointerMove = onPointerMove.bind(this)
    this._onPointerUp = onPointerUp.bind(this)
    this.domElement.addEventListener("pointerdown", this._onPointerDown as any)
    this.domElement.addEventListener("pointermove", this._onPointerHover as any)
    this.domElement.ownerDocument.addEventListener("pointerup", this._onPointerUp)
  }
  updateMatrixWorld() {
    if (this.object) {
      this.object.updateMatrixWorld()
      null === this.object.parent
        ? console.error("TransformControls: The attached 3D object must be a part of the scene graph.")
        : this.object.parent.matrixWorld.decompose(this._parentPosition, this._parentQuaternion, this._parentScale)
      this.object.matrixWorld.decompose(this.worldPosition, this.worldQuaternion, this._worldScale)
      this._parentQuaternionInv.copy(this._parentQuaternion).invert()
      this._worldQuaternionInv.copy(this.worldQuaternion).invert()
    }
    this.camera.updateMatrixWorld(),
      this.camera.matrixWorld.decompose(this.cameraPosition, this.cameraQuaternion, this._cameraScale),
      this.eye.copy(this.cameraPosition).sub(this.worldPosition).normalize(),
      super.updateMatrixWorld(true)
  }
  pointerHover(t: { x: number; y: number; button: number }) {
    if (!this.object || this.dragging) return
    s.setFromCamera(t, this.camera)
    const e = g(this._gizmo.picker[this.mode], s)
    this.axis = e ? e.object.name : null
  }
  pointerDown(t: { x: number; y: number; button: number }) {
    if (this.object && !this.dragging && !t.button && this.axis) {
      s.setFromCamera(t, this.camera)
      const e = g(this._plane, s, !0)
      if (e) {
        let t = this.space
        if (
          ("scale" === this.mode ? (t = "local") : ("E" !== this.axis && "XYZE" !== this.axis && "XYZ" !== this.axis) || (t = "world"),
          "local" === t && "rotate" === this.mode)
        ) {
          const t = this.rotationSnap
          "X" === this.axis && t && (this.object.rotation.x = Math.round(this.object.rotation.x / t) * t),
            "Y" === this.axis && t && (this.object.rotation.y = Math.round(this.object.rotation.y / t) * t),
            "Z" === this.axis && t && (this.object.rotation.z = Math.round(this.object.rotation.z / t) * t)
        }
        this.object.updateMatrixWorld()
        this.object.parent.updateMatrixWorld()
        this._positionStart.copy(this.object.position)
        this._quaternionStart.copy(this.object.quaternion)
        this._scaleStart.copy(this.object.scale)
        this.object.matrixWorld.decompose(this.worldPositionStart, this.worldQuaternionStart, this._worldScaleStart)
        this.pointStart.copy(e.point).sub(this.worldPositionStart)
      }
      this.dragging = !0
      d.mode = this.mode
      this.dispatchEvent(d)
    }
  }
  pointerMove(t: { x: number; y: number; button: number }) {
    const e = this.axis,
      i = this.mode,
      n = this.object
    let d = this.space
    if (
      ("scale" === i ? (d = "local") : ("E" !== e && "XYZE" !== e && "XYZ" !== e) || (d = "world"),
      void 0 === n || null === e || !1 === this.dragging || -1 !== t.button)
    )
      return
    s.setFromCamera(t, this.camera)
    const c = g(this._plane, s, !0)
    if (c) {
      if ((this.pointEnd.copy(c.point).sub(this.worldPositionStart), "translate" === i))
        this._offset.copy(this.pointEnd).sub(this.pointStart),
          "local" === d && "XYZ" !== e && this._offset.applyQuaternion(this._worldQuaternionInv),
          -1 === e.indexOf("X") && (this._offset.x = 0),
          -1 === e.indexOf("Y") && (this._offset.y = 0),
          -1 === e.indexOf("Z") && (this._offset.z = 0),
          "local" === d && "XYZ" !== e
            ? this._offset.applyQuaternion(this._quaternionStart).divide(this._parentScale)
            : this._offset.applyQuaternion(this._parentQuaternionInv).divide(this._parentScale),
          n.position.copy(this._offset).add(this._positionStart),
          this.translationSnap &&
            ("local" === d &&
              (n.position.applyQuaternion(o.copy(this._quaternionStart).invert()),
              -1 !== e.search("X") && (n.position.x = Math.round(n.position.x / this.translationSnap) * this.translationSnap),
              -1 !== e.search("Y") && (n.position.y = Math.round(n.position.y / this.translationSnap) * this.translationSnap),
              -1 !== e.search("Z") && (n.position.z = Math.round(n.position.z / this.translationSnap) * this.translationSnap),
              n.position.applyQuaternion(this._quaternionStart)),
            "world" === d &&
              (n.parent && n.position.add(r.setFromMatrixPosition(n.parent.matrixWorld)),
              -1 !== e.search("X") && (n.position.x = Math.round(n.position.x / this.translationSnap) * this.translationSnap),
              -1 !== e.search("Y") && (n.position.y = Math.round(n.position.y / this.translationSnap) * this.translationSnap),
              -1 !== e.search("Z") && (n.position.z = Math.round(n.position.z / this.translationSnap) * this.translationSnap),
              n.parent && n.position.sub(r.setFromMatrixPosition(n.parent.matrixWorld))))
      else if ("scale" === i) {
        if (-1 !== e.search("XYZ")) {
          let t = this.pointEnd.length() / this.pointStart.length()
          this.pointEnd.dot(this.pointStart) < 0 && (t *= -1), a.set(t, t, t)
        } else
          r.copy(this.pointStart),
            a.copy(this.pointEnd),
            r.applyQuaternion(this._worldQuaternionInv),
            a.applyQuaternion(this._worldQuaternionInv),
            a.divide(r),
            -1 === e.search("X") && (a.x = 1),
            -1 === e.search("Y") && (a.y = 1),
            -1 === e.search("Z") && (a.z = 1)
        n.scale.copy(this._scaleStart).multiply(a),
          this.scaleSnap &&
            (-1 !== e.search("X") && (n.scale.x = Math.round(n.scale.x / this.scaleSnap) * this.scaleSnap || this.scaleSnap),
            -1 !== e.search("Y") && (n.scale.y = Math.round(n.scale.y / this.scaleSnap) * this.scaleSnap || this.scaleSnap),
            -1 !== e.search("Z") && (n.scale.z = Math.round(n.scale.z / this.scaleSnap) * this.scaleSnap || this.scaleSnap))
      } else if ("rotate" === i) {
        this._offset.copy(this.pointEnd).sub(this.pointStart)
        const t = 20 / this.worldPosition.distanceTo(r.setFromMatrixPosition(this.camera.matrixWorld))
        "E" === e
          ? (this.rotationAxis.copy(this.eye),
            (this.rotationAngle = this.pointEnd.angleTo(this.pointStart)),
            this._startNorm.copy(this.pointStart).normalize(),
            this._endNorm.copy(this.pointEnd).normalize(),
            (this.rotationAngle *= this._endNorm.cross(this._startNorm).dot(this.eye) < 0 ? 1 : -1))
          : "XYZE" === e
          ? (this.rotationAxis.copy(this._offset).cross(this.eye).normalize(),
            (this.rotationAngle = this._offset.dot(r.copy(this.rotationAxis).cross(this.eye)) * t))
          : ("X" !== e && "Y" !== e && "Z" !== e) ||
            (this.rotationAxis.copy(l[e]),
            r.copy(l[e]),
            "local" === d && r.applyQuaternion(this.worldQuaternion),
            (this.rotationAngle = this._offset.dot(r.cross(this.eye).normalize()) * t)),
          this.rotationSnap && (this.rotationAngle = Math.round(this.rotationAngle / this.rotationSnap) * this.rotationSnap),
          "local" === d && "E" !== e && "XYZE" !== e
            ? (n.quaternion.copy(this._quaternionStart), n.quaternion.multiply(o.setFromAxisAngle(this.rotationAxis, this.rotationAngle)).normalize())
            : (this.rotationAxis.applyQuaternion(this._parentQuaternionInv),
              n.quaternion.copy(o.setFromAxisAngle(this.rotationAxis, this.rotationAngle)),
              n.quaternion.multiply(this._quaternionStart).normalize())
      }
      this.dispatchEvent(h)
      this.dispatchEvent(u)
    }
  }
  pointerUp(t: { x: number; y: number; button: number }) {
    0 === t.button && (this.dragging && null !== this.axis && ((c.mode = this.mode), this.dispatchEvent(c)), (this.dragging = !1), (this.axis = null))
  }
  dispose() {
    this.domElement.removeEventListener("pointerdown", this._onPointerDown as any),
      this.domElement.removeEventListener("pointermove", this._onPointerHover as any),
      this.domElement.ownerDocument.removeEventListener("pointermove", this._onPointerMove),
      this.domElement.ownerDocument.removeEventListener("pointerup", this._onPointerUp),
      this.traverse(t => {
        t["geometry"] && t["geometry"].dispose()
        t["material"] && t["material"].dispose()
      })
  }
  attach(t) {
    this.object = t
    this.visible = !0
    return this
  }
  detach() {
    this.object = null
    this.visible = !1
    this.axis = null
    return this
  }
  getMode() {
    return this.mode
  }
  setMode(t: string) {
    this.mode = t
  }
  setTranslationSnap(t) {
    this.translationSnap = t
  }
  setRotationSnap(t) {
    this.rotationSnap = t
  }
  setScaleSnap(t) {
    this.scaleSnap = t
  }
  setSize(t: number) {
    this.size = t
  }
  setSpace(t: string) {
    this.space = t
  }
  update() {
    console.warn("THREE.TransformControls: update function has no more functionality and therefore has been deprecated.")
  }
}

function getPointer(t: TouchEvent & PointerEvent) {
  if (this.domElement.ownerDocument.pointerLockElement) return { x: 0, y: 0, button: t.button }
  {
    const e = t.changedTouches ? t.changedTouches[0] : t,
      i = this.domElement.getBoundingClientRect()
    return {
      x: ((e.clientX - i.left) / i.width) * 2 - 1,
      y: (-(e.clientY - i.top) / i.height) * 2 + 1,
      button: t.button
    }
  }
}
function onPointerHover(t: PointerEvent) {
  if (this.enabled)
    switch (t.pointerType) {
      case "mouse":
      case "pen":
        this.pointerHover(this._getPointer(t))
    }
}
function onPointerDown(t: PointerEvent) {
  this.enabled &&
    ((this.domElement.style.touchAction = "none"),
    this.domElement.ownerDocument.addEventListener("pointermove", this._onPointerMove),
    this.pointerHover(this._getPointer(t)),
    this.pointerDown(this._getPointer(t)))
}
function onPointerMove(t: PointerEvent) {
  this.enabled && this.pointerMove(this._getPointer(t))
}
function onPointerUp(t: PointerEvent) {
  this.enabled &&
    ((this.domElement.style.touchAction = ""),
    this.domElement.ownerDocument.removeEventListener("pointermove", this._onPointerMove),
    this.pointerUp(this._getPointer(t)))
}
function g<A extends THREE.Object3D>(t: A, e?: THREE.Raycaster, i?: boolean): false | THREE.Intersection {
  const n = e.intersectObject(t, !0)
  for (let t = 0; t < n.length; t++) if (n[t].object.visible || i) return n[t]
  return false
}

const b = new THREE.Euler(),
  y = new THREE.Vector3(0, 1, 0),
  v = new THREE.Vector3(0, 0, 0),
  E = new THREE.Matrix4(),
  x = new THREE.Quaternion(),
  k = new THREE.Quaternion(),
  S = new THREE.Vector3(1, 0, 0),
  U = new THREE.Vector3(0, 1, 0),
  T = new THREE.Vector3(0, 0, 1)

export class TransformControlsGizmo extends THREE.Object3D {
  isTransformControlsGizmo: boolean
  picker: Record<string, THREE.Object3D>
  gizmo: Record<string, THREE.Object3D>
  helper: Record<string, THREE.Object3D>
  mode: string
  space: string
  size: number
  worldQuaternion: THREE.Quaternion
  worldPosition: THREE.Vector3
  cameraPosition: THREE.Vector3
  camera: THREE.OrthographicCamera
  eye: THREE.Vector3
  enabled: boolean
  axis: string
  dragging: boolean
  showX: boolean
  showY: boolean
  showZ: boolean
  worldPositionStart: THREE.Vector3
  rotationAxis: THREE.Vector3
  worldQuaternionStart: THREE.Quaternion
  constructor() {
    super()
    this.isTransformControlsGizmo = true
    this.type = "TransformControlsGizmo"
    const t = new THREE.MeshBasicMaterial({
      depthTest: !1,
      depthWrite: !1,
      transparent: !0,
      side: THREE.DoubleSide,
      fog: !1,
      toneMapped: !1
    })
    const e = new THREE.LineBasicMaterial({
      depthTest: !1,
      depthWrite: !1,
      transparent: !0,
      linewidth: 1,
      fog: !1,
      toneMapped: !1
    })
    const i = t.clone()
    i.opacity = 0.15
    const s = t.clone()
    s.opacity = 0.33
    const r = t.clone()
    r.color.set(16711680)
    const a = t.clone()
    a.color.set(65280)
    const o = t.clone()
    o.color.set(255)
    const l = t.clone()
    l.opacity = 0.25
    const h = l.clone()
    h.color.set(16776960)
    const d = l.clone()
    d.color.set(65535)
    const c = l.clone()
    c.color.set(16711935)
    t.clone().color.set(16776960)
    const u = e.clone()
    u.color.set(16711680)
    const p = e.clone()
    p.color.set(65280)
    const f = e.clone()
    f.color.set(255)
    const _ = e.clone()
    _.color.set(65535)
    const m = e.clone()
    m.color.set(16711935)
    const w = e.clone()
    w.color.set(16776960)
    const g = e.clone()
    g.color.set(7895160)
    const b = w.clone()
    b.opacity = 0.25
    const y = new THREE.CylinderGeometry(0, 0.05, 0.2, 12, 1, !1)
    const v = new THREE.BoxGeometry(0.125, 0.125, 0.125)
    const E = new THREE.BufferGeometry()
    function x(t, e) {
      const i = new THREE.BufferGeometry(),
        s: any = []
      for (let i = 0; i <= 64 * e; ++i) s.push(0, Math.cos((i / 32) * Math.PI) * t, Math.sin((i / 32) * Math.PI) * t)
      return i.setAttribute("position", new THREE.Float32BufferAttribute(s, 3)), i
    }
    E.setAttribute("position", new THREE.Float32BufferAttribute([0, 0, 0, 1, 0, 0], 3))
    const k = {
      X: [
        [new THREE.Mesh(y, r), [1, 0, 0], [0, 0, -Math.PI / 2], null, "fwd"],
        [new THREE.Mesh(y, r), [1, 0, 0], [0, 0, Math.PI / 2], null, "bwd"],
        [new THREE.Line(E, u)]
      ],
      Y: [
        [new THREE.Mesh(y, a), [0, 1, 0], null, null, "fwd"],
        [new THREE.Mesh(y, a), [0, 1, 0], [Math.PI, 0, 0], null, "bwd"],
        [new THREE.Line(E, p), null, [0, 0, Math.PI / 2]]
      ],
      Z: [
        [new THREE.Mesh(y, o), [0, 0, 1], [Math.PI / 2, 0, 0], null, "fwd"],
        [new THREE.Mesh(y, o), [0, 0, 1], [-Math.PI / 2, 0, 0], null, "bwd"],
        [new THREE.Line(E, f), null, [0, -Math.PI / 2, 0]]
      ],
      XYZ: [[new THREE.Mesh(new THREE.OctahedronGeometry(0.1, 0), l.clone()), [0, 0, 0], [0, 0, 0]]],
      XY: [
        [new THREE.Mesh(new THREE.PlaneGeometry(0.295, 0.295), h.clone()), [0.15, 0.15, 0]],
        [new THREE.Line(E, w), [0.18, 0.3, 0], null, [0.125, 1, 1]],
        [new THREE.Line(E, w), [0.3, 0.18, 0], [0, 0, Math.PI / 2], [0.125, 1, 1]]
      ],
      YZ: [
        [new THREE.Mesh(new THREE.PlaneGeometry(0.295, 0.295), d.clone()), [0, 0.15, 0.15], [0, Math.PI / 2, 0]],
        [new THREE.Line(E, _), [0, 0.18, 0.3], [0, 0, Math.PI / 2], [0.125, 1, 1]],
        [new THREE.Line(E, _), [0, 0.3, 0.18], [0, -Math.PI / 2, 0], [0.125, 1, 1]]
      ],
      XZ: [
        [new THREE.Mesh(new THREE.PlaneGeometry(0.295, 0.295), c.clone()), [0.15, 0, 0.15], [-Math.PI / 2, 0, 0]],
        [new THREE.Line(E, m), [0.18, 0, 0.3], null, [0.125, 1, 1]],
        [new THREE.Line(E, m), [0.3, 0, 0.18], [0, -Math.PI / 2, 0], [0.125, 1, 1]]
      ]
    }
    const A = {
      X: [[new THREE.Mesh(new THREE.CylinderGeometry(0.2, 0, 1, 4, 1, !1), i), [0.6, 0, 0], [0, 0, -Math.PI / 2]]],
      Y: [[new THREE.Mesh(new THREE.CylinderGeometry(0.2, 0, 1, 4, 1, !1), i), [0, 0.6, 0]]],
      Z: [[new THREE.Mesh(new THREE.CylinderGeometry(0.2, 0, 1, 4, 1, !1), i), [0, 0, 0.6], [Math.PI / 2, 0, 0]]],
      XYZ: [[new THREE.Mesh(new THREE.OctahedronGeometry(0.2, 0), i)]],
      XY: [[new THREE.Mesh(new THREE.PlaneGeometry(0.4, 0.4), i), [0.2, 0.2, 0]]],
      YZ: [[new THREE.Mesh(new THREE.PlaneGeometry(0.4, 0.4), i), [0, 0.2, 0.2], [0, Math.PI / 2, 0]]],
      XZ: [[new THREE.Mesh(new THREE.PlaneGeometry(0.4, 0.4), i), [0.2, 0, 0.2], [-Math.PI / 2, 0, 0]]]
    }
    const C = {
      START: [[new THREE.Mesh(new THREE.OctahedronGeometry(0.01, 2), s), null, null, null, "helper"]],
      END: [[new THREE.Mesh(new THREE.OctahedronGeometry(0.01, 2), s), null, null, null, "helper"]],
      DELTA: [
        [
          new THREE.Line(
            (function () {
              const t = new THREE.BufferGeometry()
              return t.setAttribute("position", new THREE.Float32BufferAttribute([0, 0, 0, 1, 1, 1], 3)), t
            })(),
            s
          ),
          null,
          null,
          null,
          "helper"
        ]
      ],
      X: [[new THREE.Line(E, s.clone()), [-1e3, 0, 0], null, [1e6, 1, 1], "helper"]],
      Y: [[new THREE.Line(E, s.clone()), [0, -1e3, 0], [0, 0, Math.PI / 2], [1e6, 1, 1], "helper"]],
      Z: [[new THREE.Line(E, s.clone()), [0, 0, -1e3], [0, -Math.PI / 2, 0], [1e6, 1, 1], "helper"]]
    }
    const S = {
      X: [[new THREE.Line(x(1, 0.5), u)], [new THREE.Mesh(new THREE.OctahedronGeometry(0.04, 0), r), [0, 0, 0.99], null, [1, 3, 1]]],
      Y: [
        [new THREE.Line(x(1, 0.5), p), null, [0, 0, -Math.PI / 2]],
        [new THREE.Mesh(new THREE.OctahedronGeometry(0.04, 0), a), [0, 0, 0.99], null, [3, 1, 1]]
      ],
      Z: [
        [new THREE.Line(x(1, 0.5), f), null, [0, Math.PI / 2, 0]],
        [new THREE.Mesh(new THREE.OctahedronGeometry(0.04, 0), o), [0.99, 0, 0], null, [1, 3, 1]]
      ],
      E: [
        [new THREE.Line(x(1.25, 1), b), null, [0, Math.PI / 2, 0]],
        [new THREE.Mesh(new THREE.CylinderGeometry(0.03, 0, 0.15, 4, 1, !1), b), [1.17, 0, 0], [0, 0, -Math.PI / 2], [1, 1, 0.001]],
        [new THREE.Mesh(new THREE.CylinderGeometry(0.03, 0, 0.15, 4, 1, !1), b), [-1.17, 0, 0], [0, 0, Math.PI / 2], [1, 1, 0.001]],
        [new THREE.Mesh(new THREE.CylinderGeometry(0.03, 0, 0.15, 4, 1, !1), b), [0, -1.17, 0], [Math.PI, 0, 0], [1, 1, 0.001]],
        [new THREE.Mesh(new THREE.CylinderGeometry(0.03, 0, 0.15, 4, 1, !1), b), [0, 1.17, 0], [0, 0, 0], [1, 1, 0.001]]
      ],
      XYZE: [[new THREE.Line(x(1, 1), g), null, [0, Math.PI / 2, 0]]]
    }
    const U = {
      AXIS: [[new THREE.Line(E, s.clone()), [-1e3, 0, 0], null, [1e6, 1, 1], "helper"]]
    }
    const T = {
      X: [[new THREE.Mesh(new THREE.TorusGeometry(1, 0.1, 4, 24), i), [0, 0, 0], [0, -Math.PI / 2, -Math.PI / 2]]],
      Y: [[new THREE.Mesh(new THREE.TorusGeometry(1, 0.1, 4, 24), i), [0, 0, 0], [Math.PI / 2, 0, 0]]],
      Z: [[new THREE.Mesh(new THREE.TorusGeometry(1, 0.1, 4, 24), i), [0, 0, 0], [0, 0, -Math.PI / 2]]],
      E: [[new THREE.Mesh(new THREE.TorusGeometry(1.25, 0.1, 2, 24), i)]],
      XYZE: [[new THREE.Mesh(new THREE.SphereGeometry(0.7, 10, 8), i)]]
    }
    const j = {
      X: [
        [new THREE.Mesh(v, r), [0.8, 0, 0], [0, 0, -Math.PI / 2]],
        [new THREE.Line(E, u), null, null, [0.8, 1, 1]]
      ],
      Y: [
        [new THREE.Mesh(v, a), [0, 0.8, 0]],
        [new THREE.Line(E, p), null, [0, 0, Math.PI / 2], [0.8, 1, 1]]
      ],
      Z: [
        [new THREE.Mesh(v, o), [0, 0, 0.8], [Math.PI / 2, 0, 0]],
        [new THREE.Line(E, f), null, [0, -Math.PI / 2, 0], [0.8, 1, 1]]
      ],
      XY: [
        [new THREE.Mesh(v, h), [0.85, 0.85, 0], null, [2, 2, 0.2]],
        [new THREE.Line(E, w), [0.855, 0.98, 0], null, [0.125, 1, 1]],
        [new THREE.Line(E, w), [0.98, 0.855, 0], [0, 0, Math.PI / 2], [0.125, 1, 1]]
      ],
      YZ: [
        [new THREE.Mesh(v, d), [0, 0.85, 0.85], null, [0.2, 2, 2]],
        [new THREE.Line(E, _), [0, 0.855, 0.98], [0, 0, Math.PI / 2], [0.125, 1, 1]],
        [new THREE.Line(E, _), [0, 0.98, 0.855], [0, -Math.PI / 2, 0], [0.125, 1, 1]]
      ],
      XZ: [
        [new THREE.Mesh(v, c), [0.85, 0, 0.85], null, [2, 0.2, 2]],
        [new THREE.Line(E, m), [0.855, 0, 0.98], null, [0.125, 1, 1]],
        [new THREE.Line(E, m), [0.98, 0, 0.855], [0, -Math.PI / 2, 0], [0.125, 1, 1]]
      ],
      XYZX: [[new THREE.Mesh(new THREE.BoxGeometry(0.125, 0.125, 0.125), l.clone()), [1.1, 0, 0]]],
      XYZY: [[new THREE.Mesh(new THREE.BoxGeometry(0.125, 0.125, 0.125), l.clone()), [0, 1.1, 0]]],
      XYZZ: [[new THREE.Mesh(new THREE.BoxGeometry(0.125, 0.125, 0.125), l.clone()), [0, 0, 1.1]]]
    }
    const L = {
      X: [[new THREE.Mesh(new THREE.CylinderGeometry(0.2, 0, 0.8, 4, 1, !1), i), [0.5, 0, 0], [0, 0, -Math.PI / 2]]],
      Y: [[new THREE.Mesh(new THREE.CylinderGeometry(0.2, 0, 0.8, 4, 1, !1), i), [0, 0.5, 0]]],
      Z: [[new THREE.Mesh(new THREE.CylinderGeometry(0.2, 0, 0.8, 4, 1, !1), i), [0, 0, 0.5], [Math.PI / 2, 0, 0]]],
      XY: [[new THREE.Mesh(v, i), [0.85, 0.85, 0], null, [3, 3, 0.2]]],
      YZ: [[new THREE.Mesh(v, i), [0, 0.85, 0.85], null, [0.2, 3, 3]]],
      XZ: [[new THREE.Mesh(v, i), [0.85, 0, 0.85], null, [3, 0.2, 3]]],
      XYZX: [[new THREE.Mesh(new THREE.BoxGeometry(0.2, 0.2, 0.2), i), [1.1, 0, 0]]],
      XYZY: [[new THREE.Mesh(new THREE.BoxGeometry(0.2, 0.2, 0.2), i), [0, 1.1, 0]]],
      XYZZ: [[new THREE.Mesh(new THREE.BoxGeometry(0.2, 0.2, 0.2), i), [0, 0, 1.1]]]
    }
    const R = {
      X: [[new THREE.Line(E, s.clone()), [-1e3, 0, 0], null, [1e6, 1, 1], "helper"]],
      Y: [[new THREE.Line(E, s.clone()), [0, -1e3, 0], [0, 0, Math.PI / 2], [1e6, 1, 1], "helper"]],
      Z: [[new THREE.Line(E, s.clone()), [0, 0, -1e3], [0, -Math.PI / 2, 0], [1e6, 1, 1], "helper"]]
    }
    function O(t) {
      const e = new THREE.Object3D()
      for (const i in t)
        for (let n = t[i].length; n--; ) {
          const s = t[i][n][0].clone()
          const r = t[i][n][1]
          const a = t[i][n][2]
          const o = t[i][n][3]
          const l = t[i][n][4]
          s.name = i
          s["tag"] = l
          r && s.position.set(r[0], r[1], r[2])
          a && s.rotation.set(a[0], a[1], a[2])
          o && s.scale.set(o[0], o[1], o[2])
          s.updateMatrix()
          const h = s.geometry.clone()
          h.applyMatrix4(s.matrix)
          s.geometry = h
          s.renderOrder = 1 / 0
          s.position.set(0, 0, 0)
          s.rotation.set(0, 0, 0)
          s.scale.set(1, 1, 1)
          e.add(s)
        }
      return e
    }
    this.gizmo = {}
    this.picker = {}
    this.helper = {}
    this.add((this.gizmo.translate = O(k)))
    this.add((this.gizmo.rotate = O(S)))
    this.add((this.gizmo.scale = O(j)))
    this.add((this.picker.translate = O(A)))
    this.add((this.picker.rotate = O(T)))
    this.add((this.picker.scale = O(L)))
    this.add((this.helper.translate = O(C)))
    this.add((this.helper.rotate = O(U)))
    this.add((this.helper.scale = O(R)))
    this.picker.translate.visible = !1
    this.picker.rotate.visible = !1
    this.picker.scale.visible = !1
  }
  updateMatrixWorld(t: boolean) {
    const e = "local" === ("scale" === this.mode ? "local" : this.space) ? this.worldQuaternion : k
    this.gizmo.translate.visible = "translate" === this.mode
    this.gizmo.rotate.visible = "rotate" === this.mode
    this.gizmo.scale.visible = "scale" === this.mode
    this.helper.translate.visible = "translate" === this.mode
    this.helper.rotate.visible = "rotate" === this.mode
    this.helper.scale.visible = "scale" === this.mode
    let i: THREE.Object3D[] = [...this.picker[this.mode].children, ...this.gizmo[this.mode].children, ...this.helper[this.mode].children]
    for (let t = 0; t < i.length; t++) {
      const s = i[t]
      let a: number
      s.visible = !0
      s.rotation.set(0, 0, 0)
      s.position.copy(this.worldPosition)
      a = this.camera.isOrthographicCamera
        ? (this.camera.top - this.camera.bottom) / this.camera.zoom
        : this.worldPosition.distanceTo(this.cameraPosition) * Math.min((1.9 * Math.tan((Math.PI * this.camera["fov"]) / 360)) / this.camera.zoom, 7)
      s.scale.set(1, 1, 1).multiplyScalar((a * this.size) / 7)
      if ("helper" !== s["tag"]) {
        if ((s.quaternion.copy(e), "translate" === this.mode || "scale" === this.mode)) {
          const t = 0.99,
            i = 0.2,
            n = 0
          ;("X" !== s.name && "XYZX" !== s.name) ||
            (Math.abs(y.copy(S).applyQuaternion(e).dot(this.eye)) > t && (s.scale.set(1e-10, 1e-10, 1e-10), (s.visible = !1))),
            ("Y" !== s.name && "XYZY" !== s.name) ||
              (Math.abs(y.copy(U).applyQuaternion(e).dot(this.eye)) > t && (s.scale.set(1e-10, 1e-10, 1e-10), (s.visible = !1))),
            ("Z" !== s.name && "XYZZ" !== s.name) ||
              (Math.abs(y.copy(T).applyQuaternion(e).dot(this.eye)) > t && (s.scale.set(1e-10, 1e-10, 1e-10), (s.visible = !1))),
            "XY" === s.name && Math.abs(y.copy(T).applyQuaternion(e).dot(this.eye)) < i && (s.scale.set(1e-10, 1e-10, 1e-10), (s.visible = !1)),
            "YZ" === s.name && Math.abs(y.copy(S).applyQuaternion(e).dot(this.eye)) < i && (s.scale.set(1e-10, 1e-10, 1e-10), (s.visible = !1)),
            "XZ" === s.name && Math.abs(y.copy(U).applyQuaternion(e).dot(this.eye)) < i && (s.scale.set(1e-10, 1e-10, 1e-10), (s.visible = !1)),
            -1 !== s.name.search("X") &&
              (y.copy(S).applyQuaternion(e).dot(this.eye) < n
                ? "fwd" === s["tag"]
                  ? (s.visible = !1)
                  : (s.scale.x *= -1)
                : "bwd" === s["tag"] && (s.visible = !1)),
            -1 !== s.name.search("Y") &&
              (y.copy(U).applyQuaternion(e).dot(this.eye) < n
                ? "fwd" === s["tag"]
                  ? (s.visible = !1)
                  : (s.scale.y *= -1)
                : "bwd" === s["tag"] && (s.visible = !1)),
            -1 !== s.name.search("Z") &&
              (y.copy(T).applyQuaternion(e).dot(this.eye) < n
                ? "fwd" === s["tag"]
                  ? (s.visible = !1)
                  : (s.scale.z *= -1)
                : "bwd" === s["tag"] && (s.visible = !1))
        } else
          "rotate" === this.mode &&
            (x.copy(e),
            y.copy(this.eye).applyQuaternion(o.copy(e).invert()),
            -1 !== s.name.search("E") && s.quaternion.setFromRotationMatrix(E.lookAt(this.eye, v, U)),
            "X" === s.name && (o.setFromAxisAngle(S, Math.atan2(-y.y, y.z)), o.multiplyQuaternions(x, o), s.quaternion.copy(o)),
            "Y" === s.name && (o.setFromAxisAngle(U, Math.atan2(y.x, y.z)), o.multiplyQuaternions(x, o), s.quaternion.copy(o)),
            "Z" === s.name && (o.setFromAxisAngle(T, Math.atan2(y.y, y.x)), o.multiplyQuaternions(x, o), s.quaternion.copy(o)))
        ;(s.visible = s.visible && (-1 === s.name.indexOf("X") || this.showX)),
          (s.visible = s.visible && (-1 === s.name.indexOf("Y") || this.showY)),
          (s.visible = s.visible && (-1 === s.name.indexOf("Z") || this.showZ)),
          (s.visible = s.visible && (-1 === s.name.indexOf("E") || (this.showX && this.showY && this.showZ))),
          (s["material"]._opacity = s["material"]._opacity || s["material"].opacity),
          (s["material"]._color = s["material"]._color || s["material"].color.clone()),
          s["material"].color.copy(s["material"]._color),
          (s["material"].opacity = s["material"]._opacity),
          this.enabled
            ? this.axis &&
              (s.name === this.axis ||
              this.axis.split("").some(function (t) {
                return s.name === t
              })
                ? ((s["material"].opacity = 1), s["material"].color.lerp(new THREE.Color(1, 1, 1), 0.5))
                : ((s["material"].opacity *= 0.25), s["material"].color.lerp(new THREE.Color(1, 1, 1), 0.5)))
            : ((s["material"].opacity *= 0.5), s["material"].color.lerp(new THREE.Color(1, 1, 1), 0.5))
      } else
        (s.visible = !1),
          "AXIS" === s.name
            ? (s.position.copy(this.worldPositionStart),
              (s.visible = !!this.axis),
              "X" === this.axis &&
                (o.setFromEuler(b.set(0, 0, 0)),
                s.quaternion.copy(e).multiply(o),
                Math.abs(y.copy(S).applyQuaternion(e).dot(this.eye)) > 0.9 && (s.visible = !1)),
              "Y" === this.axis &&
                (o.setFromEuler(b.set(0, 0, Math.PI / 2)),
                s.quaternion.copy(e).multiply(o),
                Math.abs(y.copy(U).applyQuaternion(e).dot(this.eye)) > 0.9 && (s.visible = !1)),
              "Z" === this.axis &&
                (o.setFromEuler(b.set(0, Math.PI / 2, 0)),
                s.quaternion.copy(e).multiply(o),
                Math.abs(y.copy(T).applyQuaternion(e).dot(this.eye)) > 0.9 && (s.visible = !1)),
              "XYZE" === this.axis &&
                (o.setFromEuler(b.set(0, Math.PI / 2, 0)),
                y.copy(this.rotationAxis),
                s.quaternion.setFromRotationMatrix(E.lookAt(v, y, U)),
                s.quaternion.multiply(o),
                (s.visible = this.dragging)),
              "E" === this.axis && (s.visible = !1))
            : "START" === s.name
            ? (s.position.copy(this.worldPositionStart), (s.visible = this.dragging))
            : "END" === s.name
            ? (s.position.copy(this.worldPosition), (s.visible = this.dragging))
            : "DELTA" === s.name
            ? (s.position.copy(this.worldPositionStart),
              s.quaternion.copy(this.worldQuaternionStart),
              r.set(1e-10, 1e-10, 1e-10).add(this.worldPositionStart).sub(this.worldPosition).multiplyScalar(-1),
              r.applyQuaternion(this.worldQuaternionStart.clone().invert()),
              s.scale.copy(r),
              (s.visible = this.dragging))
            : (s.quaternion.copy(e),
              this.dragging ? s.position.copy(this.worldPositionStart) : s.position.copy(this.worldPosition),
              this.axis && (s.visible = -1 !== this.axis.search(s.name)))
    }
    super.updateMatrixWorld(t)
  }
}
